package com.tencreat.service.mall.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tencreat.common.constant.Lock4jConstant;
import com.tencreat.common.core.domain.BaseEntity;
import com.tencreat.common.core.domain.entity.MallMessage;
import com.tencreat.common.utils.SecurityUtils;
import com.tencreat.common.utils.StringUtils;
import com.tencreat.domain.mall.MallMessageConfig;
import com.tencreat.domain.mall.MallMessageList;
import com.tencreat.domain.mall.MallMessageRecord;
import com.tencreat.domain.mall.vo.MallMessageListVO;
import com.tencreat.mapper.mall.MallMessageListMapper;
import com.tencreat.mapper.mall.MallMessageRecordMapper;
import com.tencreat.service.mall.IMallMessageConfigService;
import com.tencreat.service.mall.IMallMessageListService;
import com.tencreat.service.mall.IMallMessageRecordService;
import com.tencreat.service.mall.IMallMessageService;
import com.tencreat.socket.NettyWebSocket;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.yeauty.pojo.Session;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 联系客服消息处理
 *
 * @author zixin
 */
@Service
@RequiredArgsConstructor
public class MallMessageServiceImpl implements IMallMessageService {

    private final IMallMessageListService messageListService;
    private final IMallMessageRecordService messageRecordService;
    private final MallMessageRecordMapper messageRecordMapper;
    private final MallMessageListMapper messageListMapper;
    private final RedisTemplate redisTemplate;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final IMallMessageConfigService mallMessageConfigService;

    /**
     * 上次清除心跳时间
     */
    private static LocalDateTime lastClearTime = null;

    /**
     * @param userId     发送消息ID
     * @param joinUserId 接收消息ID
     * @param message    消息体
     * @param type       0|发送者列表，1|接收者列表
     * @return
     */
    private MallMessageList generateMessageList(String userId, String joinUserId, MallMessage message, Integer type) {
        MallMessageList messageList = new MallMessageList();
        messageList.setCreateTime(message.getSendTime());
        messageList.setUpdateTime(message.getSendTime());
        messageList.setUnreadMessage(0L);
        // 创建消息发送者列表
        if (type == 0) {
            messageList.setUserId(NumberUtil.parseLong(userId));
            messageList.setJoinUserId(NumberUtil.parseLong(joinUserId));
            // 平台用户关联ID为0
            if (message.getType() == 0) {
                messageList.setJoinUserId(0L);
            } else {
                messageList.setUserId(0L);
            }
        }
        // 创建消息接收者列表
        else {
            messageList.setUserId(NumberUtil.parseLong(joinUserId));
            messageList.setJoinUserId(NumberUtil.parseLong(userId));
            // 平台用户发送者ID为0
            if (message.getType() == 0) {
                messageList.setUserId(0L);
            } else {
                messageList.setJoinUserId(0L);
            }
        }
        messageListService.save(messageList);
        return messageList;
    }

    @Override
    public void sendMessage(Session session, String userId, String message) {
        Map<String, Session> clients = NettyWebSocket.clients;
        MallMessage mallMessage = verifyMessage(userId, message);
        if (mallMessage == null) {
            session.sendText("参数格式错误：" + message);
            return;
        }
        // 非发消息的其他操作类型
        if (mallMessage.getOperationType() != null) {
            switch (mallMessage.getOperationType()) {
                case 1:
                    // pc端获取消息列表
                    pcGetMessageList(session, mallMessage);
                    return;
                case 2:
                    // app端获取消息列表
                    appGetMessageList(session, mallMessage, userId);
                    return;
                case 3:
                    // 心跳
                    mallMessage.setMsg("心跳返回");
                    session.setAttribute("nowTime", LocalDateTime.now());
                    session.sendText(JSON.toJSONString(mallMessage));
                    return;
                case 4:
                    // 创建会话
                    createMessageList(mallMessage, userId);
                    return;
                case 5:
                    // pc端获取消息数量
                    pcGetMessageCount(session, mallMessage);
                    return;
                default:
                    session.sendText("未知操作类型：" + message);
                    return;
            }
        } else {
            if (StringUtils.isEmpty(mallMessage.getJoinUserId())) {
                mallMessage.setJoinUserId("1");
            }

            redisTemplate.convertAndSend(Lock4jConstant.IM_MESSAGE, mallMessage);
        }

    }

    /**
     * 清除无用链接
     */
    @Override
    @Lock4j
    public void clearUnSocket(Map<String, Session> clients) {
        LocalDateTime now = LocalDateTime.now();
        if (lastClearTime == null || lastClearTime.plusMinutes(2).compareTo(now) < 0) {
            lastClearTime = now;
            Set<String> set = clients.keySet();
            for (String key : set) {
                Session sessionTemp = clients.get(key);
                if (sessionTemp != null) {
                    LocalDateTime nowTime = sessionTemp.getAttribute("nowTime");
                    // 超过2分钟无反应（剔除）
                    if (nowTime == null || nowTime.plusMinutes(2).compareTo(now) < 0) {
                        clients.remove(key);
                        sessionTemp.close();
                    }
                } else {
                    clients.remove(key);
                }
            }
        }
    }

    @Lock4j(keys = "#mallMessage.msgId", acquireTimeout = 1)
    @Override
    public void messageHandling(MallMessage mallMessage) {
        Map<String, Session> clients = NettyWebSocket.clients;
        String userId = mallMessage.getUserId();
        LocalDateTime now = mallMessage.getSendTime();
        // App端发送消息
        if (mallMessage.getType() == 0) {
            // 判断是否在自动回复时间内
            MallMessageConfig mallMessageConfig = messageListMapper.queryMessageConfig(LocalDateTime.now());
            if (mallMessageConfig != null) {
                MallMessageRecord autoRecord = new MallMessageRecord();
                for (String key : clients.keySet()) {
                    String[] keyValue = key.split(":");
                    String userIdTemp = keyValue[0];
                    if (userIdTemp.equals(userId)) {
                        autoRecord = new MallMessageRecord();
                        autoRecord.setUserId(0L);
                        autoRecord.setJoinUserId(NumberUtil.parseLong(userId));
                        autoRecord.setCreateTime(now);
                        autoRecord.setMsg(mallMessageConfig.getContent());
                        autoRecord.setMsgType(0);
                        autoRecord.setMsgId(mallMessage.getMsgId());
                        Session receiptSession = clients.get(key);
                        receiptSession.sendText(JSON.toJSONString(autoRecord));
                    }
                }
                // 自动回复消息
                messageRecordService.save(autoRecord);
            }
            // 查询所有在线平台用户（平台用户规则id长度小于5）
            for (String key : clients.keySet()) {
                String[] keyValue = key.split(":");
                String userIdTemp = keyValue[0];
                // 平台用户
                if (userIdTemp.length() < 5) {
                    // 给平台用户发送消息
                    Session receiptSession = clients.get(key);
                    String message = JSON.toJSONString(mallMessage);
                    receiptSession.sendText(message);
                }
            }
        }
        // 平台端发送消息
        else {
            for (String key : clients.keySet()) {
                String[] keyValue = key.split(":");
                String userIdTemp = keyValue[0];
                if (userIdTemp.equals(mallMessage.getJoinUserId())) {
                    // 平台用户
                    Session receiptSession = clients.get(key);
                    // 用户存在就发送
                    if (receiptSession != null) {
                        String message = JSON.toJSONString(mallMessage);
                        receiptSession.sendText(message);
                    }
                }
            }
        }

    }

    /**
     * 创建会话
     *
     * @param mallMessage
     * @param userId
     */
    public void createMessageList(MallMessage mallMessage, String userId) {
        // 发送者消息列表
        MallMessageList sendMallMessageList = messageListService.getOne(
                new LambdaQueryWrapper<MallMessageList>()
                        .eq(MallMessageList::getUserId, mallMessage.getType() == 0 ? mallMessage.getUserId() : 0)
                        .eq(MallMessageList::getJoinUserId, mallMessage.getType() == 0 ? 0 : mallMessage.getJoinUserId()), false);
        // 接收者消息列表
        MallMessageList receiverMallMessageList = messageListService.getOne(
                new LambdaQueryWrapper<MallMessageList>()
                        .eq(MallMessageList::getUserId, mallMessage.getType() == 0 ? 0 : mallMessage.getJoinUserId())
                        .eq(MallMessageList::getJoinUserId, mallMessage.getType() == 0 ? mallMessage.getUserId() : 0), false);

        if (sendMallMessageList == null) {
            sendMallMessageList = generateMessageList(userId, mallMessage.getJoinUserId(), mallMessage, 0);
        }
        if (receiverMallMessageList == null) {
            receiverMallMessageList = generateMessageList(userId, mallMessage.getJoinUserId(), mallMessage, 1);
        }
    }

    @Lock4j(keys = "#mallMessage.userId")
    @Override
    public void messageListHandling(MallMessage mallMessage) {
        String userId = mallMessage.getUserId();
        LocalDateTime now = mallMessage.getSendTime();

        // 发送者消息列表
        MallMessageList sendMallMessageList = messageListService.getOne(
                new LambdaQueryWrapper<MallMessageList>()
                        .eq(MallMessageList::getUserId, mallMessage.getType() == 0 ? mallMessage.getUserId() : 0)
                        .eq(MallMessageList::getJoinUserId, mallMessage.getType() == 0 ? 0 : mallMessage.getJoinUserId()), false);
        // 接收者消息列表
        MallMessageList receiverMallMessageList = messageListService.getOne(
                new LambdaQueryWrapper<MallMessageList>()
                        .eq(MallMessageList::getUserId, mallMessage.getType() == 0 ? 0 : mallMessage.getJoinUserId())
                        .eq(MallMessageList::getJoinUserId, mallMessage.getType() == 0 ? mallMessage.getUserId() : 0), false);

        if (sendMallMessageList == null) {
            sendMallMessageList = generateMessageList(userId, mallMessage.getJoinUserId(), mallMessage, 0);
        }
        if (receiverMallMessageList == null) {
            receiverMallMessageList = generateMessageList(userId, mallMessage.getJoinUserId(), mallMessage, 1);
        }
        // 更新消息列表显示
        sendMallMessageList.setNewMsg(mallMessage.getMsg());
        sendMallMessageList.setNewMsgType(mallMessage.getMsgType());
        sendMallMessageList.setUpdateTime(now);
        receiverMallMessageList.setNewMsg(mallMessage.getMsg());
        receiverMallMessageList.setNewMsgType(mallMessage.getMsgType());
        receiverMallMessageList.setUpdateTime(now);
        receiverMallMessageList.setUnreadMessage(receiverMallMessageList.getUnreadMessage() + 1);
        // 消息记录
        MallMessageRecord messageRecord = new MallMessageRecord();
        messageRecord.setUserId(NumberUtil.parseLong(userId));
        messageRecord.setJoinUserId(NumberUtil.parseLong(mallMessage.getJoinUserId()));
        messageRecord.setCreateTime(now);
        messageRecord.setMsg(mallMessage.getMsg());
        messageRecord.setMsgId(mallMessage.getMsgId());
        messageRecord.setMsgType(mallMessage.getMsgType());
        messageRecord.setDuration(mallMessage.getDuration());

        messageListService.updateById(sendMallMessageList);
        messageListService.updateById(receiverMallMessageList);
        messageRecordService.save(messageRecord);
        boolean isExist = NettyWebSocket.isExist(mallMessage.getJoinUserId());
        if (isExist) {
            messageHandling(mallMessage);
        }
    }

    @Override
    public void clearAppUnreadMessage() {
        Long userId = SecurityUtils.getLoginUser().getAccountLogin().getUuid();
        messageListMapper.clearAppUnreadMessage(userId);
        messageListMapper.clearAppUnreadStatus(userId);
    }

    @Override
    public void clearUnreadMessage(Long userId) {
        MallMessageList messageList = messageListService.getOne(new LambdaQueryWrapper<MallMessageList>()
                .eq(MallMessageList::getUserId, 0)
                .eq(MallMessageList::getJoinUserId, userId));
        messageList.setUnreadMessage(0L);
        messageListService.updateById(messageList);
        // 修改消息状态
        messageRecordService.lambdaUpdate().eq(MallMessageRecord::getUserId, userId)
                .set(MallMessageRecord::getUnreadStatus, 1)
                .update();
    }

    @Override
    public Integer getAppUnreadMessage() {
        Long userId = SecurityUtils.getLoginUser().getAccountLogin().getUuid();
        return messageListMapper.getAppUnreadMessage(userId);
    }

    @Override
    public Page<MallMessage> pageMessage(Page page, Long userId, LocalDateTime time) {
        Page<MallMessage> mallMessagePage = messageRecordMapper.pageMessage(page, userId, time);
        // 获取记录的头像
        MallMessageConfig messageConfig = mallMessageConfigService.lambdaQuery().last("limit 1").one();
        mallMessagePage.getRecords().forEach(item ->{
            // 后台账号默认客服头像
            if (item.getUserId().length() < 6){
                item.setPhoto(messageConfig.getHeadUrl());
            }else {
                if (item.getAvatar() != null){
                    item.setPhoto(item.getAvatar());
                }
            }

        });
        return mallMessagePage;
    }

    @Override
    public Page<MallMessageRecord> appMessage(Page page, Long userId) {
        return messageRecordMapper.appMessage(page, userId);
    }

    @Override
    public List<MallMessageListVO> selectList(Integer isToday, String mobile) {
        return messageListService.selectList(isToday, mobile);
    }

    @Override
    public Integer receptionToday() {
        return messageListMapper.receptionToday();
    }

    public List<MallMessageListVO> listByUserId(Long userId) {
        return messageListService.listByUserId(userId);
    }

    void pcGetMessageList(Session session, MallMessage message) {
        List<MallMessageListVO> list = selectList(message.getIsToday(), message.getMobile());
        message.setOperationParam(list);
        session.sendText(JSON.toJSONString(message));
    }

    void pcGetMessageCount(Session session, MallMessage message) {
        Long messageSum = messageListService.unreadMessageSum();
        message.setUnreadMessage(messageSum);
        session.sendText(JSON.toJSONString(message));
    }

    void appGetMessageList(Session session, MallMessage message, String userId) {
        List<MallMessageListVO> list = listByUserId(Long.valueOf(userId));
        message.setOperationParam(list);
        session.sendText(JSON.toJSONString(message));
    }

    /**
     * 校验入参
     */
    MallMessage verifyMessage(String userId, String message) {
        try {
            MallMessage mallMessage = JSON.parseObject(message, MallMessage.class);
            if (mallMessage != null) {
                if (!userId.equals(mallMessage.getUserId())) {
                    return null;
                }
            }
            mallMessage.setMsgId(IdUtil.objectId());
            mallMessage.setSendTime(LocalDateTime.now());
            return mallMessage;
        } catch (Exception e) {
            return null;
        }
    }
}
